#ifndef __BASIC_CODER__
#define __BASIC_CODER__

namespace xm
{
	namespace coder
	{
		enum { INVALID_CODE_SIZE = -1 };
		template < SIZE_T BUFFER_SIZE = 0x20000 >
		class CoderLL : public IKBCoderLL
		{
		public:
			iptr<IKBStream>		_outs;
			IKBCoderHook *		_hook;
			BYTE				_out_buf[BUFFER_SIZE];
			CoderLL() : _hook(0)
			{
			}
			STDMETHOD(Start)( IUnknown * outs, IKBCoderHook * hook )
			{
				_outs = outs;
				_hook = hook;
				memset( _out_buf, 0, sizeof(_out_buf) );
				return S_OK;
			}
			STDMETHOD(CodeIt)( LPCVOID lpBuffer, SIZE_T cbBuffer )
			{
				if(!lpBuffer) return E_INVALIDARG;
				if(!cbBuffer) return S_OK;

				UINT8 * bufToProcess = (UINT8 *)lpBuffer;
				SIZE_T cbToProcess = cbBuffer;
				for( ; cbToProcess;  )
				{
					SIZE_T cbOutSize = BUFFER_SIZE;
					SIZE_T cbProcessed = _CodeFrame(bufToProcess,cbToProcess,_out_buf,cbOutSize);
					if( cbProcessed == INVALID_CODE_SIZE ) 
						return E_FAIL;
					RFAILED( __AfterDataProcessed(cbProcessed,bufToProcess,cbOutSize,_out_buf) );
					bufToProcess += cbProcessed;
					cbToProcess -= cbProcessed;
				}
				return S_OK;
			}
			STDMETHOD(End)()
			{
				_outs.dispose();
				_hook = 0;
				return S_OK;
			}
		protected:
			HRESULT __AfterDataProcessed( SIZE_T cbInput, LPCVOID lpInBuf, SIZE_T cbOutput, LPCVOID lpOutBuf )
			{
				if( _outs && cbOutput ) 
				{
					size_t eff = 0;
					HRESULT hr = _outs->Write( lpOutBuf, cbOutput, &eff );
					if( hr < 0 ) return hr;
					if( eff != cbOutput ) return S_FALSE;
				}
				if( !_hook ) return S_OK;
				cblob_t ib(lpInBuf,cbInput), ob( lpOutBuf, cbOutput );
				return ( _hook->OnCodeFrame( ib, ob ) );
			}
			// return 0 means FAILED!
			virtual SIZE_T _CodeFrame( LPCVOID lpInBuf, SIZE_T cbIn, LPVOID lpOutBuf, SIZE_T & cbOut ) = 0;
		};
	};



};



#endif